package com.zbmy.plugin.im.server.core.net;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.PooledByteBufAllocator;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.timeout.IdleStateHandler;

import java.net.InetSocketAddress;

import com.zbmy.plugin.im.server.core.NetShareInstance;
import com.zbmy.plugin.im.server.core.codec.ProtocolDecoder;
import com.zbmy.plugin.im.server.core.codec.ProtocolEncoder;
import com.zbmy.plugin.im.server.core.handler.IoHandler;

/***
 * 
 * Netty服务端   
 * @ClassName: RabbitShardServer    
 * @author 杨乐平    
 * @date 2018年3月28日 上午10:51:51    
 * @version  v 1.0
 */
public class NettyServer {
	private static final int MAX_FRAME_LENGTH = 1024 * 1024*10;  //最大长度
    private static final int LENGTH_FIELD_LENGTH = 4;  //长度字段所占的字节数
    private static final int LENGTH_FIELD_OFFSET = 5;  //长度偏移
    private static final int LENGTH_ADJUSTMENT = 0;
    private static final int INITIAL_BYTES_TO_STRIP = 0;
    
	/**
	 * 
	 * 启动Netty服务器
	 * @author 杨乐平    
	 * @throws Exception 
	 * @Title: start     
	 * @Return: void 返回值
	 */
	public void startUp() throws Exception{
		new Thread(){
			@Override
			public void run() {
				//bossEventLoopGroup线程监听端口，workerEventLoopGroup线程负责数据读写
				EventLoopGroup bossEventLoopGroup = new NioEventLoopGroup();
				// 线程太多的话每个线程得到的时间片就少，CPU要让各个线程都有机会执行就要切换，切换就要不断保存和还原线程的上下文现场。
				//于是检查Netty的I/O worker的EventLoopGroup。之前在《Netty 4源码解析：服务端启动》中曾经分析过，EventLoopGroup默认的线程数是CPU核数的二倍。
				//所以手动配置NioEventLoopGroup的线程数，减少一些I/O线程。
				EventLoopGroup workerEventLoopGroup = new NioEventLoopGroup(6);
				//辅助启动类
		        ServerBootstrap serverBootstrap = new ServerBootstrap();
		        try {
		        	 //设置线程池
		            serverBootstrap.group(bossEventLoopGroup, workerEventLoopGroup);
		            //设置socket工厂
		            serverBootstrap.channel(NioServerSocketChannel.class);
		            // 设置监听端口  
		            serverBootstrap.localAddress(new InetSocketAddress(NetShareInstance.frontenServerPort));
		            //设置TCP参数
		            //1.链接缓冲池的大小（ServerSocketChannel的设置）
		            serverBootstrap.option(ChannelOption.SO_BACKLOG,1024*1024);
		            //维持链接的活跃，清除死链接(SocketChannel的设置)
		            serverBootstrap.childOption(ChannelOption.SO_KEEPALIVE,true);
		            //关闭延迟发送
		            serverBootstrap.childOption(ChannelOption.TCP_NODELAY,true);
		            // 用Netty带来的又一个好处就是内存管理。只需一行简单的配置，就能获得到内存池带来的好处。
		            // 在底层，Netty实现了一个Java版的Jemalloc内存管理库（还记得Redis自带的那个吗），为我们做完了所有“脏活累活”！
		            serverBootstrap. childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);
		            // 使用内存池之后，内存的申请和释放必须成对出现，即retain()和release()要成对出现，否则会导致内存泄露。
		            // 如果使用内存池，完成ByteBuf的解码工作之后必须显式的调用ReferenceCountUtil.release(msg)对接收缓冲区ByteBuf进行内存释放，否则它会被认为仍然在使用中，这样会导致内存泄露。
		            //设置管道工厂
		            serverBootstrap.childHandler(new ChildChannelHandler());
		            ChannelFuture f = serverBootstrap.bind().sync();// 配置完成，开始绑定server，通过调用sync同步方法阻塞直到绑定成功  
		            System.out.println(NettyServer.class.getName() + " started and listen on " + f.channel().localAddress());  
		            f.channel().closeFuture().sync();// 应用程序会一直等待，直到channel关闭  
				} catch (Exception e) {
					e.printStackTrace();
				}finally{
					// 释放资源
					bossEventLoopGroup.shutdownGracefully();  
					workerEventLoopGroup.shutdownGracefully(); 
				}
			}}.start();
		
	}
	
	private class ChildChannelHandler extends ChannelInitializer<SocketChannel>{
		@Override
		protected void initChannel(SocketChannel arg0) throws Exception {
			ChannelPipeline pipeline = arg0.pipeline();
			pipeline.addLast(new ProtocolDecoder());						// 包解码器, 已经自定义做了粘包处理 OK使用
			//pipeline.addLast(new PacketLengthFieldDecoder(MAX_FRAME_LENGTH,LENGTH_FIELD_OFFSET,LENGTH_FIELD_LENGTH,0,0)); // 自带粘包处理 OK使用
			pipeline.addLast(new ProtocolEncoder());						// 包编码器
			pipeline.addLast("idleStateHandler", new IdleStateHandler(0, 0, 300));//客户端300秒没收发包，便会触发UserEventTriggered事件到MessageTransportHandler
			pipeline.addLast(new IoHandler());
		}
	}
}
